package pr.codegen.util; /**
 * Copyright © 2019 dream horse Info. Tech Ltd. All rights reserved.
 *
 * @Package: com.gitee.mybatis.fl.util
 * @author: flying-cattle
 * @date: 2019年4月9日 下午8:15:25
 */


import common.GenTableColumn;
import pr.codegen.PrPathesConstant;
import pr.codegen.PrGenConstants;
import pr.codegen.TemplateProxy;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.Velocity;
import org.apache.velocity.app.VelocityEngine;

import java.awt.*;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.StringSelection;
import java.awt.datatransfer.Transferable;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.StringWriter;
import java.sql.*;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashSet;
import java.util.List;

@Slf4j
public class EntityInfoUtilForPR {


    public static String database = "PLUSHAM_HOTLE_DATA";
    public static String url = "jdbc:mysql://localhost:3306/" + database + "?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&serverTimezone=GMT%2B8";
    public static String name = "root";
    public static String pass = "zhouman123";


//	public static GenTable transferGenTable(GenTable param) {
//		String operName = "localRun";
//		GenTable genTable = null;
//		try {
//			String tableName = param.getTableName();
//			genTable = GenUtils.initClassNames(param, operName);
//			genTable.setTplCategory(GenConstants.TPL_CRUD); // 默认 crud
//
//			// 保存列信息
//			List<GenTableColumn> genTableColumns = genTableColumnMapper.selectDbTableColumnsByName(tableName);
//			for (GenTableColumn column : genTableColumns) {
//				GenUtils.initColumnField(column, genTable);
//			}
//			genTable.setColumns(genTableColumns);
//		} catch (Exception e) {
//			throw new CustomException("导入失败：" + e.getMessage());
//		}
//		return genTable;
//	}

    public static PrGenTable readTable(String tableName, PrPathesConstant pathesConstant) throws IOException {
//        Properties properties = PropertiesLoaderUtils.loadAllProperties("application-local.properties");
        // 创建连接
        Connection con = null;
        PreparedStatement pstemt = null;
        ResultSet rs = null;
        //sql
        // select table_name, table_comment, create_time, update_time from information_schema.tables
        //		where table_name NOT LIKE 'qrtz_%' and table_name NOT LIKE 'gen_%' and table_schema = (select database())
        //		and table_name = ''

        String sql = "select table_name, table_comment, create_time, update_time from information_schema.tables" +
                " where table_schema='" + database + "' and table_name='" + tableName + "'";
        try {
            PrGenTable genTable = new PrGenTable();
            con = DriverManager.getConnection(url, name, pass);
            pstemt = con.prepareStatement(sql);
            rs = pstemt.executeQuery();

            boolean autoRemovePre = true;
            String tablePrefix = "";
//            gen.author=ruoyi
//# 默认生成包路径 system 需改成自己的模块名称 如 system monitor tool
//            gen.packageName=com.ruoyi.project.business
//# 自动去除表前缀，默认是true
//            gen.autoRemovePre=true
//# 表前缀（生成类名不会包含表前缀，多个用逗号分隔）
//            gen.tablePrefix=schedule_


            while (rs.next()) {
                String tableNameDB = rs.getString(1);
                String tableComment = rs.getString(2);
                String businessName = PrGenUtils.getBusinessName(tableName);
                String className = PrGenUtils.convertClassName(tableName, autoRemovePre, tablePrefix);
                String moduleName = PrGenUtils.getModuleName(pathesConstant.packageName);
                String functionName = PrGenUtils.replaceText(tableComment);

                genTable.setTableName(tableName);
                genTable.setTableComment(tableComment);
                genTable.setTplCategory(PrGenConstants.TPL_CRUD); // 默认 crud
                genTable.setClassName(className);
                genTable.setPackageName(pathesConstant.packageName);
                genTable.setServicePackageName(pathesConstant.servicePackageName);
                genTable.setModuleName(moduleName);
                genTable.setBusinessName(businessName);
                genTable.setFunctionName(functionName);
//        genTable.setFunctionAuthor(GenConfig.getAuthor());
//                genTable.setCreateBy("someone");
                genTable.setDomainPackage(pathesConstant.domaainPackage);
                genTable.setMapperPackage(pathesConstant.mapperPackage);
                genTable.setRequestPackage(pathesConstant.requestPackage);
                genTable.setResponsePackage(pathesConstant.responsePackage);


                List<GenTableColumn> genTableColumns = readColumn(con, tableName);
                genTable.setColumns(genTableColumns);
                setPkColumn(genTable, genTableColumns);

            }
            if(genTable == null) {
                throw new RuntimeException("未获取到表信息");
            }

            // 完成后关闭
            rs.close();
            pstemt.close();
            con.close();
            return genTable;
        } catch (Exception e) {
            log.error("error sql: {}", sql, e);
            throw new RuntimeException("自动生成实体类错误：" + e.getMessage());
        } finally {
            try {
                if (rs != null) rs.close();
            } catch (SQLException se2) {
            }
            // 关闭资源
            try {
                if (pstemt != null) pstemt.close();
            } catch (SQLException se2) {
            }// 什么都不做
            try {
                if (con != null) con.close();
            } catch (SQLException se) {
                se.printStackTrace();
            }
        }
    }

    public static void setPkColumn(PrGenTable table, List<GenTableColumn> columns) {
        for (GenTableColumn column : columns) {
            if (column.isPk()) {
                table.setPkColumn(column);
                break;
            }
        }
        if (com.ruoyi.common.utils.StringUtils.isNull(table.getPkColumn())) {
            table.setPkColumn(columns.get(0));
        }
    }

    public static List<GenTableColumn> readColumn(Connection con, String tableName) throws SQLException {
        List<GenTableColumn> columns = new ArrayList<>();
        // 创建连接
        PreparedStatement pstemt = null;
        ResultSet rs = null;
        //sql
        // select column_name, (case when (is_nullable = 'no' <![CDATA[ && ]]> column_key != 'PRI') then '1' else null end) as is_required, (case when column_key = 'PRI' then '1' else '0' end) as is_pk, ordinal_position as sort, column_comment, (case when extra = 'auto_increment' then '1' else '0' end) as is_increment, column_type
        //		from information_schema.columns where table_schema = (select database()) and table_name = (#{tableName})
        //		order by ordinal_position
        String sql = "select column_name, " +
                "(case when (is_nullable = 'no' && column_key != 'PRI') then '1' else null end) as is_required, " +
                "(case when column_key = 'PRI' then '1' else '0' end) as is_pk, " +
                "ordinal_position as sort, " +
                "column_comment, " +
                "(case when extra = 'auto_increment' then '1' else '0' end) as is_increment, " +
                "column_type " +
                "from information_schema.columns where table_schema='" + database + "' and table_name='" + tableName + "' " +
                "order by ordinal_position";
        try {
            pstemt = con.prepareStatement(sql);
            rs = pstemt.executeQuery();
            while (rs.next()) {
                String columnName = rs.getString(1);
                String isRequired = rs.getString(2);
                String isPk = rs.getString(3);
                Integer sort = rs.getInt(4);
                String columnComment = rs.getString(5);
                String isIncrement = rs.getString(6);
                String columnType = rs.getString(7);

                GenTableColumn column = new GenTableColumn();
                column.setColumnName(columnName);
                column.setIsRequired(isRequired);
                column.setIsPk(isPk);
                column.setSort(sort);
                column.setColumnComment(columnComment);
                column.setIsIncrement(isIncrement);
                column.setColumnType(columnType);


                initColumnField(column);
                columns.add(column);
            }

            if (null == columns || columns.size() == 0) {
                throw new RuntimeException("未能读取到表或表中的字段。请检查链接url，数据库账户，数据库密码，查询的数据名、是否正确。");
            }
            return columns;
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException("自动生成实体类错误：" + e.getMessage());
        } finally {
            try {
                if (rs != null) rs.close();
            } catch (SQLException se2) {
            }
            // 关闭资源
            try {
                if (pstemt != null) pstemt.close();
            } catch (SQLException se2) {
            }// 什么都不做

        }
    }


    /**
     * 初始化列属性字段
     */
    public static void initColumnField(GenTableColumn column) {
        String dataType = PrGenUtils.getDbType(column.getColumnType());
        String columnName = column.getColumnName();
        // 设置java字段名
        column.setJavaField(StringUtils.toCamelCase(columnName));

        if (PrGenUtils.arraysContains(PrGenConstants.COLUMNTYPE_STR, dataType)) {
            column.setJavaType(PrGenConstants.TYPE_STRING);
            // 字符串长度超过500设置为文本域
            Integer columnLength = PrGenUtils.getColumnLength(column.getColumnType());
            String htmlType = columnLength >= 500 ? PrGenConstants.HTML_TEXTAREA : PrGenConstants.HTML_INPUT;
            column.setHtmlType(htmlType);
        } else if (PrGenUtils.arraysContains(PrGenConstants.COLUMNTYPE_TIME, dataType)) {
            column.setJavaType(PrGenConstants.TYPE_TIME);
            column.setHtmlType(PrGenConstants.HTML_DATETIME);
        } else if (PrGenUtils.arraysContains(PrGenConstants.COLUMNTYPE_DATE_TIME, dataType)) {
            column.setJavaType(PrGenConstants.TYPE_DATE_TIME);
            column.setHtmlType(PrGenConstants.HTML_DATETIME);
        } else if (PrGenUtils.arraysContains(PrGenConstants.COLUMNTYPE_DATE, dataType)) {
            column.setJavaType(PrGenConstants.TYPE_DATE);
            column.setHtmlType(PrGenConstants.HTML_DATETIME);
        } else if (PrGenUtils.arraysContains(PrGenConstants.COLUMNTYPE_BIT, dataType)) {
            column.setJavaType(PrGenConstants.TYPE_BOOLEAN);
        } else if (PrGenUtils.arraysContains(PrGenConstants.COLUMNTYPE_NUMBER, dataType)) {
            column.setHtmlType(PrGenConstants.HTML_INPUT);

            // 如果是浮点型 统一用BigDecimal
            String[] str = StringUtils.split(StringUtils.substringBetween(column.getColumnType(), "(", ")"), ",");
            if (str != null && str.length == 2 && Integer.parseInt(str[1]) > 0) {
                column.setJavaType(PrGenConstants.TYPE_BIGDECIMAL);
            }
            // 如果是整形
            else if (str != null && str.length == 1 && Integer.parseInt(str[0]) <= 10) {
                column.setJavaType(PrGenConstants.TYPE_INTEGER);
            }
            // 长整形
            else {
                column.setJavaType(PrGenConstants.TYPE_LONG);
            }
        }

        // 插入字段（默认所有字段都需要插入）
        column.setIsInsert(PrGenConstants.REQUIRE);

        // 编辑字段
        if (!PrGenUtils.arraysContains(PrGenConstants.COLUMNNAME_NOT_EDIT, columnName) && !column.isPk()) {
            column.setIsEdit(PrGenConstants.REQUIRE);
        }
        // 列表字段
        if (!PrGenUtils.arraysContains(PrGenConstants.COLUMNNAME_NOT_LIST, columnName) && !column.isPk()) {
            column.setIsList(PrGenConstants.REQUIRE);
        }
        // 查询字段
        if (!PrGenUtils.arraysContains(PrGenConstants.COLUMNNAME_NOT_QUERY, columnName) && !column.isPk()) {
            column.setIsQuery(PrGenConstants.REQUIRE);
        }

        // 查询字段类型
        if (StringUtils.endsWithIgnoreCase(columnName, "name")) {
            column.setQueryType(PrGenConstants.QUERY_LIKE);
        } else if (PrGenUtils.arraysContains(PrGenConstants.COLUMNNAME_EQ_TYPE, columnName) && !column.isPk()) {
            column.setQueryType(PrGenConstants.EQ);
        }
        // 状态字段设置单选框
        if (StringUtils.endsWithIgnoreCase(columnName, "status")) {
            column.setHtmlType(PrGenConstants.HTML_RADIO);
        }
        // 类型&性别字段设置下拉框
        else if (StringUtils.endsWithIgnoreCase(columnName, "type")
                || StringUtils.endsWithIgnoreCase(columnName, "sex")) {
            column.setHtmlType(PrGenConstants.HTML_SELECT);
        }
    }



    public static void generatorCodeToDisk(String tableName , PrPathesConstant pathesConstant) {
        PrGenTable table = null;
        try {
            table = EntityInfoUtilForPR.readTable(tableName, pathesConstant);
        } catch (IOException e) {
            log.error("tableName 生成代码异常: " , e);
        }
        if(table == null) {
            throw new RuntimeException("读取表信息异常");
        }


        PrVelocityInitializer.initVelocity();
        VelocityContext context = prepareContext(table);

        String osName = System.getProperty("os.name");
        String slash = "\\";
        if(StringUtils.containsIgnoreCase(osName, "mac")
                || StringUtils.containsIgnoreCase(osName, "linux")) {
            slash = "/";
        }

        List<TemplateProxy> templateModelList = getTemplateModelList(context,  pathesConstant);
        for (TemplateProxy templateProxy : templateModelList) {

            String fileName = templateProxy.getFileName();
            String template = templateProxy.getTmpFilePath();
            // 渲染模板
            StringWriter sw = new StringWriter();
            Template tpl = Velocity.getTemplate(template, PrGenConstants.UTF8);
            tpl.merge(context, sw);

            FileOutputStream fos = null;
            try {
                File file = new File(fileName);
                if (!file.getParentFile().exists()) {
                    file.getParentFile().mkdirs();
                }
                fos = new FileOutputStream(file);
                log.info("生成文件 fileName {}", fileName);
                IOUtils.write(sw.toString(), fos, PrGenConstants.UTF8);
                IOUtils.closeQuietly(sw);
            } catch (IOException e) {
                log.error("渲染模板失败，表名：{}, template: {}", table.getTableName(), template, e);
            } finally {
                if (fos != null) {
                    try {
                        fos.close();
                    } catch (IOException e) {
                        log.error("fos close failed", e);
                    }
                }
            }
        }
    }



    public static void generatorCodeToClipboard(String tableName , PrPathesConstant pathesConstant) {

        PrGenTable table = null;
        try {
            table = EntityInfoUtilForPR.readTable(tableName, pathesConstant);
        } catch (IOException e) {
            log.error("tableName 生成代码异常: " , e);
        }
        if(table == null) {
            throw new RuntimeException("读取表信息异常");
        }


        PrVelocityInitializer.initVelocity();
        VelocityContext context = prepareContext(table);
        VelocityEngine velocity = new VelocityEngine();
        velocity.init();

        TemplateProxy templateProxy = genSetMethod( pathesConstant);
        String template = templateProxy.getTmpFilePath();
        // 渲染模板
        StringWriter sw = new StringWriter();
        Template tpl = Velocity.getTemplate(template, PrGenConstants.UTF8);
        tpl.merge(context, sw);

        velocity.evaluate(context, sw, "", "");
        setSysClipboardText(sw.toString());
    }

    public static void setSysClipboardText(String writeMe) {
        Clipboard clip = Toolkit.getDefaultToolkit().getSystemClipboard();
        Transferable tText = new StringSelection(writeMe);
        clip.setContents(tText, null);
    }


    /**
     * 获取模板信息
     *
     * @return 模板列表
     */
    public static List<TemplateProxy> getTemplateModelList(VelocityContext context, PrPathesConstant pathesConstant) {

        String className = context.get("ClassName").toString();
        String addRequestClass = context.get("AddRequestClass").toString();
        String responseClass = context.get("ResponseClass").toString();


        List<TemplateProxy> templateProxies = new ArrayList<>();


        if(pathesConstant.genController) {
            // TODO
        }

        if(pathesConstant.genMapper) {
            TemplateProxy entity = new TemplateProxy();
            entity.setTmpFilePath(pathesConstant.vmPath + "/entity.java.vm");
            entity.setFileName(StringUtils.format("{}/{}.java", pathesConstant.domainPath, className));
            templateProxies.add(entity);

            TemplateProxy mapperJava = new TemplateProxy();
            mapperJava.setTmpFilePath(pathesConstant.vmPath + "/mapper.java.vm");
            mapperJava.setFileName(StringUtils.format("{}/{}Mapper.java", pathesConstant.mapperPath, className));
            templateProxies.add(mapperJava);


            TemplateProxy mapperXml = new TemplateProxy();
            mapperXml.setTmpFilePath(pathesConstant.vmPath + "/mapper.xml.vm");
            mapperXml.setFileName(StringUtils.format("{}/{}Mapper.xml", pathesConstant.mapperXmlPath, className));
            templateProxies.add(mapperXml);
        }

        if(pathesConstant.genService) {
            TemplateProxy serviceService = new TemplateProxy();
            serviceService.setTmpFilePath(pathesConstant.vmPath + "/service.java.vm");
            serviceService.setFileName(StringUtils.format("{}/{}Service.java", pathesConstant.servicePath, className));
            templateProxies.add(serviceService);

            TemplateProxy serviceServiceImpl = new TemplateProxy();
            serviceServiceImpl.setTmpFilePath(pathesConstant.vmPath + "/serviceImpl.java.vm");
            serviceServiceImpl.setFileName(StringUtils.format("{}/{}ServiceImpl.java", pathesConstant.serviceImplPath, className));
            templateProxies.add(serviceServiceImpl);
        }

        return templateProxies;
    }

    public static TemplateProxy genSetMethod(PrPathesConstant pathesConstant) {
        TemplateProxy entity = new TemplateProxy();
        entity.setTmpFilePath(pathesConstant.vmPath + "/setmethod.java.vm");
//        entity.setFileName(StringUtils.format("{}/{}.java", PathesConstant.domainPath + "/src/main/java/com/pr/report/domain", className));
        return entity;
    }




    /**
     * 设置模板变量信息
     *
     * @return 模板列表
     */
    public static VelocityContext prepareContext(PrGenTable genTable) {
        String moduleName = genTable.getModuleName();
        String businessName = genTable.getBusinessName();
        String packageName = genTable.getPackageName();
        String servicePackageName = genTable.getServicePackageName();
        String tplCategory = genTable.getTplCategory();
        String functionName = genTable.getFunctionName();
        String domainPackage = genTable.getDomainPackage();
        String mapperPackage = genTable.getMapperPackage();
        String requestPackage = genTable.getRequestPackage();
        String responsePackage = genTable.getResponsePackage();
        String className = genTable.getClassName();


        VelocityContext velocityContext = new VelocityContext();
        velocityContext.put("tplCategory", genTable.getTplCategory());
        velocityContext.put("tableName", genTable.getTableName());
        velocityContext.put("functionName", StringUtils.isNotEmpty(functionName) ? functionName : "【请填写功能名称】");
        velocityContext.put("ClassName", className);
        velocityContext.put("className", StringUtils.uncapitalize(className));
        velocityContext.put("moduleName", genTable.getModuleName());
        velocityContext.put("BusinessName", StringUtils.capitalize(genTable.getBusinessName()));
        velocityContext.put("businessName", genTable.getBusinessName());
        velocityContext.put("basePackage", PrVelocityUtils.getPackagePrefix(packageName));
        velocityContext.put("packageName", packageName);
        velocityContext.put("servicePackageName", servicePackageName);
        velocityContext.put("author", genTable.getFunctionAuthor());
        velocityContext.put("datetime", DateUtils.getDate(new Date()));
        velocityContext.put("pkColumn", genTable.getPkColumn());
        velocityContext.put("importList", getImportList(genTable.getColumns()));
        velocityContext.put("permissionPrefix", PrVelocityUtils.getPermissionPrefix(moduleName, businessName));
        velocityContext.put("columns", genTable.getColumns());
        velocityContext.put("table", genTable);
        velocityContext.put("domainPackage", domainPackage);
        velocityContext.put("mapperPackage", mapperPackage);
        velocityContext.put("requestPackage", requestPackage);
        velocityContext.put("responsePackage", responsePackage);
        velocityContext.put("AddRequestClass", "Add" + className);
        velocityContext.put("ResponseClass", "Response" + className);
//        if (GenConstants.TPL_TREE.equals(tplCategory)) {
//            VelocityUtils.setTreeVelocityContext(velocityContext, genTable);
//        }
        return velocityContext;
    }

    public static HashSet<String> getImportList(List<GenTableColumn> columns) {
        HashSet<String> importList = new HashSet<String>();
        for (GenTableColumn column : columns) {
            if (!column.isSuperColumn() && PrGenConstants.TYPE_DATE_TIME.equals(column.getJavaType())) {
                importList.add("java.time.LocalDateTime");
                importList.add("com.fasterxml.jackson.annotation.JsonFormat");
            } else if (!column.isSuperColumn() && PrGenConstants.TYPE_DATE.equals(column.getJavaType())) {
                importList.add("java.time.LocalDate");
                importList.add("com.fasterxml.jackson.annotation.JsonFormat");
            } else if (!column.isSuperColumn() && PrGenConstants.TYPE_TIME.equals(column.getJavaType())) {
                importList.add("java.time.LocalTime");
                importList.add("com.fasterxml.jackson.annotation.JsonFormat");

            } else if (!column.isSuperColumn() && PrGenConstants.TYPE_BIGDECIMAL.equals(column.getJavaType())) {
                importList.add("java.math.BigDecimal");
            }
        }
        return importList;
    }
}
